package com.qingyun.gateway.filter;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.qingyun.common.dto.UserDTO;
import cn.hutool.core.bean.BeanUtil;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.cache.CaffeineCacheMetrics;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.qingyun.common.util.RedisConstants.LOGIN_USER_KEY;
import static com.qingyun.common.util.RedisConstants.LOGIN_USER_TTL;

/**
 * 认证过滤器 - 验证JWT Token
 */
@Slf4j
@Component
public class AuthFilter implements GlobalFilter, Ordered {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    // 不需要认证的路径
    private static final List<String> SKIP_AUTH_PATHS = Arrays.asList(
        "/user/login"
    );
    private final Cache<String, UserDTO> cache;

    @Autowired
    public AuthFilter(MeterRegistry meterRegistry) {
        this.cache = Caffeine.newBuilder()
                .expireAfterWrite(1, TimeUnit.HOURS)
                .maximumSize(100)
                .recordStats()
                .build();
        CaffeineCacheMetrics.monitor(meterRegistry, this.cache, "UserInfo_cache");
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取请求路径
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();

        // 检查是否需要跳过认证
        if (shouldSkipAuth(path)) {
            return chain.filter(exchange);
        }

        // 获取Authorization头
        String authHeader = request.getHeaders().getFirst("Authorization");
        
        if (!StringUtils.hasText(authHeader) || !authHeader.startsWith("Bearer ")) {
            log.warn("Missing or invalid Authorization header for path: {}", path);
            return unauthorized(exchange);
        }

        // 提取Token
        String token = authHeader.substring(7);

        // 先检查本地缓存
        UserDTO userDTO = cache.getIfPresent(token);
        String redisKey= LOGIN_USER_KEY + token;
        if (userDTO == null) {
            // 本地缓存没有，查询Redis
            try {
                Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(redisKey);
                if (userMap != null && !userMap.isEmpty()) {
                    userDTO = BeanUtil.toBean(userMap, UserDTO.class);
                    // 存入本地缓存
                    cache.put(token, userDTO);
                    log.debug("更新Redis中用户信息过期时间");
                } else {
                    log.warn("Token无效，Redis中不存在: {}", token);
                    return unauthorized(exchange);
                }
            } catch (Exception e) {
                log.error("查询Redis失败: {}", e.getMessage());
                return unauthorized(exchange);
            }
        } else {
            log.debug("从本地缓存获取用户信息: {}", userDTO.getId());
        }

        // 将用户信息添加到请求头中
        ServerHttpRequest mutatedRequest = request.mutate()
            .header("X-User-Id", userDTO.getId().toString())
            .header("X-User-Name", userDTO.getNickName())
            .header("X-User-Permission", userDTO.getPermission().toString())
            .build();
        stringRedisTemplate.expire(redisKey,LOGIN_USER_TTL, TimeUnit.MINUTES);
        log.info("更新Redis中用户信息过期时间");
        return chain.filter(exchange.mutate().request(mutatedRequest).build());
    }

    private boolean shouldSkipAuth(String path) {
        return SKIP_AUTH_PATHS.stream().anyMatch(path::startsWith);
    }


    private Mono<Void> unauthorized(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        
        String body = "{\"code\":401,\"message\":\"Unauthorized\",\"success\":false}";
        return response.writeWith(Mono.just(response.bufferFactory().wrap(body.getBytes())));
    }

    @Override
    public int getOrder() {
        return 0; // 在日志过滤器之后执行
    }
}
